/**
 * Copyright (C) BFH www.bfh.ch 2011
 * Code written by: Patrick Dobler, Marc Folly
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ch.bfh.ti.kybernetik.gui.slick;

import java.awt.Point;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.vecmath.Vector2d;

import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import ch.bfh.ti.kybernetik.gui.common.RoboterColorManager;
import ch.bfh.ti.kybernetik.simulator.controller.Simulator;
import ch.bfh.ti.kybernetik.simulator.controller.SimulatorCommand;
import ch.bfh.ti.kybernetik.simulator.controller.ThreadSafeIterationAction;
import ch.bfh.ti.kybernetik.simulator.controller.roboter.RoboterController;
import ch.bfh.ti.kybernetik.simulator.controller.roboter.RoboterMoveState;
import ch.bfh.ti.kybernetik.simulator.model.Roboter;

public class GuiSimulator extends BasicGame implements Observer {

	private static final int RENDER_UPDATE_RATE = 60;

	private Image roboterImage = null;

	private Graphics roboterImageGraphics;

	private Simulator simulator;

	private Point mouseClickedPointPosition = new Point(0, 0);

	private RoboterColorManager roboterColor = RoboterColorManager.getInstance();

	public GuiSimulator(Simulator simulator) {
		super("Kybernetik Simulator");
		this.simulator = simulator;
		this.simulator.addObserver(this);
	}

	@Override
	public void init(final GameContainer gc) throws SlickException {
		// roboterImage = new Image("plane.png");
		// roboterImageGraphics = roboterImage.getGraphics();
		simulator.setWidth(gc.getWidth());
		simulator.setHeight(gc.getHeight());
		gc.setAlwaysRender(true);
		gc.setTargetFrameRate(RENDER_UPDATE_RATE);
		gc.setMaximumLogicUpdateInterval(1 / RENDER_UPDATE_RATE);
		gc.setMaximumLogicUpdateInterval(1 / RENDER_UPDATE_RATE);
		simulator.iterateRoboterControllers(new ThreadSafeIterationAction<RoboterController>() {
			@Override
			public void performAction(RoboterController roboterController) {
				//System.out.println("GUI INIT: Iterate RoboterController");
				Roboter roboter = roboterController.getRoboter();
				roboter.setX(gc.getWidth() / 2);
				roboter.setY(gc.getHeight() / 2);
				roboter.setDirection(new Vector2d(0, 1));
			}
		});

	}

	@Override
	public void update(GameContainer gc, int delta) throws SlickException {
		final Input input = gc.getInput();

		if (input.isKeyDown(Input.KEY_S)) {
			simulator.setSimulating(true);
		} else if (input.isKeyDown(Input.KEY_Q)) {
			simulator.setSimulating(false);
		} else if (input.isMouseButtonDown(0)) {
			mouseClickedPointPosition.setLocation(input.getMouseX(), input.getMouseY());
			simulator.iterateRoboterControllers(new ThreadSafeIterationAction<RoboterController>() {
				@Override
				public void performAction(RoboterController roboterController) {
					Roboter roboter = roboterController.getRoboter();
					Vector2d vec = new Vector2d(roboter.getX(), roboter.getY(), input.getMouseX(), input.getMouseY());
					vec.normalize();
					roboter.setDirection(vec);

				}
			});

		}
	}

	@Override
	public void render(GameContainer gc, final Graphics g) throws SlickException {
		final double directionPointLength = 100;
		g.setAntiAlias(true);
		simulator.iterateRoboterControllers(new ThreadSafeIterationAction<RoboterController>() {
			@Override
			public void performAction(RoboterController roboterController) {
				//System.out.println("GUI RENDER GAME: IterateRoboterController");
				final Roboter roboter = roboterController.getRoboter();

				// renderNextRoboterMoves(g, directionPointLength,
				// roboterController);

				// Draw Tracks
				//renderRoboterTrack(g, directionPointLength, roboterController);

				// Draw Current Roboter Position
				renderCurrentRoboter(g, directionPointLength, roboter);

			}
		});
	}

	private void renderCurrentRoboter(Graphics g, final double directionPointLength, final Roboter roboter) {
		java.awt.Color c = this.roboterColor.getRoboterColor(roboter);
		Color roboterColer = convertToSlickColor(c);
		g.setColor(roboterColer);
		g.fillOval((float) roboter.getX() - 3, (float) roboter.getY() - 3, 6, 6);
		Vector2d directionVector = roboter.getDirection();
		double currentDirectionPointX = roboter.getX() + directionPointLength * directionVector.getX();
		double currentDirectionPointY = roboter.getY() + directionPointLength * directionVector.getY();
		g.drawLine((float) roboter.getX(), (float) roboter.getY(), (float) currentDirectionPointX, (float) currentDirectionPointY);
	}

	private Color convertToSlickColor(java.awt.Color c) {
		Color roboterColer = new Color(c.getRed(), c.getGreen(), c.getBlue());
		return roboterColer;
	}

	private void renderRoboterTrack(final Graphics g, double directionPointLength, final RoboterController roboterController) {
		roboterController.iterateRoboterMoveStates(new ThreadSafeIterationAction<RoboterMoveState>() {
			@Override
			public void performAction(RoboterMoveState roboterMoveState) {
				//System.out.println("GUI RENDER TRACK: Iterate RoboterMoveState");
				Color roboterColer = convertToSlickColor(RoboterColorManager.getInstance().getRoboterColor(roboterController.getRoboter()));
				g.setColor(roboterColer);
				g.drawLine((float) roboterMoveState.getRoboterX(), (float) roboterMoveState.getRoboterY(),
						(float) roboterMoveState.getNewRobotorPositionForwardX(), (float) roboterMoveState.getNewRobotorPositionForwardY());

			}
		});
	}

	private void renderNextRoboterMoves(Graphics g, final double directionPointLength, RoboterController roboterController) {
		for (RoboterMoveState roboterMoveState : roboterController.calculateEstimatedRoboterMoveStates(4)) {
			// Draw Current Roboter Direction (Angle) Line
			g.setColor(Color.red);
			float currentDirectionPointX = (float) (roboterMoveState.getRoboterX() + directionPointLength
					* roboterMoveState.getCurrentDirectionVector().getX());
			float currentDirectionPointY = (float) (roboterMoveState.getRoboterY() + directionPointLength
					* roboterMoveState.getCurrentDirectionVector().getY());
			g.drawLine((float) roboterMoveState.getRoboterX(), (float) roboterMoveState.getRoboterY(), currentDirectionPointX,
					currentDirectionPointY);

			// Draw Current Roboter Position
			g.setColor(Color.green);
			g.fillOval((float) roboterMoveState.getRoboterX() - 3, (float) roboterMoveState.getRoboterY() - 3, 6, 6);

			// Draw Bottom Line
			g.setColor(Color.yellow);
			g.drawLine((float) roboterMoveState.getRoboterX(), (float) roboterMoveState.getRoboterY(),
					(float) roboterMoveState.getBottomLeftPointX(), (float) roboterMoveState.getBottomLeftPointY());
			g.drawLine((float) roboterMoveState.getRoboterX(), (float) roboterMoveState.getRoboterY(),
					(float) roboterMoveState.getBottomRightPointX(), (float) roboterMoveState.getBottomRightPointY());

			// Draw Left Motor Speed Line
			g.setColor(Color.yellow);
			g.drawLine((float) roboterMoveState.getBottomLeftPointX(), (float) roboterMoveState.getBottomLeftPointY(),
					(float) roboterMoveState.getMotorSpeedLeftPointX(), (float) roboterMoveState.getMotorSpeedLeftPointY());
			// Draw Right Motor Speed Line
			g.drawLine((float) roboterMoveState.getBottomRightPointX(), (float) roboterMoveState.getBottomRightPointY(),
					(float) roboterMoveState.getMotorSpeedRightPointX(), (float) roboterMoveState.getMotorSpeedRightPointY());

			// Draw Top Line
			g.drawLine((float) roboterMoveState.getMotorSpeedRightPointX(), (float) roboterMoveState.getMotorSpeedRightPointY(),
					(float) roboterMoveState.getMotorSpeedLeftPointX(), (float) roboterMoveState.getMotorSpeedLeftPointY());

			// Draw New Position
			g.setColor(Color.red);
			g.fillOval((float) roboterMoveState.getNewRobotorPositionForwardX() - 3,
					(float) roboterMoveState.getNewRobotorPositionForwardY() - 3, 6, 6);

			// Draw new Roboter Direction (Angle) Line
			double newDirectionPointX = roboterMoveState.getNewRobotorPositionForwardX() + directionPointLength
					* roboterMoveState.getNewDirectionVector().getX();
			double newDirectionPointY = roboterMoveState.getNewRobotorPositionForwardY() + directionPointLength
					* roboterMoveState.getNewDirectionVector().getY();

			g.setColor(Color.magenta);
			g.drawLine((float) roboterMoveState.getNewRobotorPositionForwardX(), (float) roboterMoveState.getNewRobotorPositionForwardY(),
					(float) newDirectionPointX, (float) newDirectionPointY);
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		SimulatorCommand simulatorCommand = (SimulatorCommand) arg;
		if (simulatorCommand == SimulatorCommand.MOVED) {
			// this.simulator.setSimulating(false);

		}
	}
}